package zhongbo.java.air.monitor.service.impl;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.springframework.stereotype.Service;
import springfox.documentation.spring.web.json.Json;
import zhongbo.java.air.monitor.dto.PointDto;
import zhongbo.java.air.monitor.service.IJtsService;
import zhongbo.java.air.monitor.util.JtsUtil;

import java.util.List;
import java.util.stream.Collectors;

/**
 * JTS（Java Topology Suite）服务实现类，用于处理多边形区域和点的相关逻辑。
 *
 * @author 钟波dellNotebook
 * @since 2025年04月09日 星期三 06:40:59
 */
@Service
@Slf4j
public class JtsServiceImpl implements IJtsService {

    /*
     * 多边形至少需要4个点数据，第一个数据和最后一个数据一致，表示点位闭合。
     * 比如三角形最少3个点，再加一个结束闭合点，一共需要4个点；
     * 示例：
     * 如: [[0,0],[0,1],[1,1],[0,0]]
     * 多边形示例:
     * [
     *     {"longitude":115.95,"latitude":40.45},
     *     {"longitude":115.95,"latitude":39.48},
     *     {"longitude":117.2,"latitude":39.41},
     *     {"longitude":117.49,"latitude":40.41},
     *     {"longitude":115.95,"latitude":40.45}
     * ]
     */

    /**
     * 解析告警区域顶点字符串为 PointDto 列表。
     * <p>
     * 输入的 vertices 必须是 JSON 数组格式，包含多个经纬度对象。
     *
     * @param vertices 告警区域顶点的 JSON 字符串
     * @return 解析后的 PointDto 列表
     */
    @Override
    public List<PointDto> parseAlertZoneVertices(String vertices) {
        JSONArray jsonArray = JSONUtil.parseArray(vertices);
        List<PointDto> pointList = jsonArray.toList(PointDto.class);
        if (!validAreaPointList(pointList)) {
            return null;
        }
        return pointList;
    }

    /**
     * 校验多边形点位列表是否合法。
     * <p>
     * 合法条件包括：列表不为空、点数不少于4个、首尾点重合。
     *
     * @param pointList 多边形点位列表
     * @return 如果校验通过返回 true，否则返回 false
     */
    private boolean validAreaPointList(List<PointDto> pointList) {
        if (pointList == null) {
            log.error("校验失败! 多边形点位数据为 null");
            return false;
        }
        if (pointList.size() < 4) {
            log.error("校验失败! 多边形点位数据数量错误: {}", pointList);
            return false;
        }
        PointDto firstPoint = pointList.get(0);
        PointDto lastPoint = pointList.get(pointList.size() - 1);
        if (!firstPoint.equals(lastPoint)) {
            log.error("校验失败! 多边形点位数据数量错误: {}", pointList);
            return false;
        }
        return true;
    }

    /**
     * 判断给定的经纬度坐标是否在指定的告警区域内。
     * <p>
     * 需要确保输入的多边形点位列表合法。
     *
     * @param pointList 区域顶点列表
     * @param longitude 经度
     * @param latitude  纬度
     * @return 如果点在区域内返回 true，否则返回 false
     */
    @Override
    public boolean isInAlertZone(List<PointDto> pointList, Double longitude, Double latitude) {
        if (!validAreaPointList(pointList)) {
            throw new RuntimeException("无效的区域多边形");
        }
        Polygon polygon = convertToPolygon(pointList);
        Point point = JtsUtil.createPoint(longitude, latitude);
        return JtsUtil.isPointInPolygon(point, polygon);
    }


    /**
     * 将 PointDto 列表转换为 JTS 的 Polygon 对象。
     *
     * @param pointList 区域顶点列表
     * @return 转换后的 Polygon 对象
     */
    private Polygon convertToPolygon(List<PointDto> pointList) {
        List<Coordinate> collect = pointList.stream().map(e -> {
            Double longitude = e.getLongitude();
            Double latitude = e.getLatitude();
            return JtsUtil.createCoordinate(longitude, latitude);
        }).collect(Collectors.toList());

        Coordinate[] coordinates = ArrayUtil.toArray(collect, Coordinate.class);

        return JtsUtil.createPolygon(coordinates);
    }
}
